/*
* Copyright (c) 2011 Connection Road.  All rights reserved.
* This program and the accompanying material are made available
* under the terms of the Eclipse Public Licnese v1.0 which is
* available at http://www.eclipse.org/legal/epl-v10.html
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.AccessControl;
using Microsoft.Synchronization.Files;
using SyncToy;

namespace CrSyncFilesUtils
{
    /// <summary>
    /// Convenience class for managing and intefacing with SyncToy's store file. 
    /// </summary>
    public class CrSyncToyManager
    {
        public CrSyncToyManager(bool enable)
        {
            EnableSyncToyIntegration = enable;
        }
        
        public SyncEngineConfig CurrentFolderPair { get; set; }
        public bool EnableSyncToyIntegration { get; set; }
        private int CurrentIndex { get; set; }


        /// <summary>
        /// Validates if the FileSyncPair info object has an identical pair in SyncToy's database.
        /// The identity only matches if both souce and destination folders has the same unique identifer regardless of
        /// folder pair name. Use HasFolderPairNamed() instead if you wish to look for the existence of a folder pair name.
        /// </summary>
        /// <param name="fileSyncPairInfo"></param>
        /// <returns></returns>
        public bool HasFolderPair(FileSyncPairInfo fileSyncPairInfo)
        {
            var hasMirror = false;
            if(fileSyncPairInfo.ExtendedInfo.ContainsKey("HAS_MIRROR"))
            {
                hasMirror = (bool)fileSyncPairInfo.ExtendedInfo.GetObjectForKey("HAS_MIRROR");
            }

            var hasFolderPair = EnableSyncToyIntegration && (fileSyncPairInfo.ImportedFromSyncToy || hasMirror);

            if (hasFolderPair)
            {
                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();
                foreach (var syncEngineConfig in availablePairs)
                {
                    var isLeftUidEqual = ( syncEngineConfig.LeftGuid.ToString() ==
                                         fileSyncPairInfo.LeftFolder.UniqueIdentifier )
                                         ? true : false ;
                    var isRightUidEqual = (syncEngineConfig.RightGuid.ToString() ==
                                           fileSyncPairInfo.RightFolder.UniqueIdentifier)
                                              ? true
                                              : false;

                   if( isRightUidEqual && isLeftUidEqual)
                   {
                        CurrentFolderPair = syncEngineConfig;
                        CurrentIndex = availablePairs.IndexOf(CurrentFolderPair);
                        return true;
                   }
                }
            }
            return false;
        }

        public bool HasFolderPair(SyncEngineConfig syncEngineConfig)
        {
            return HasFolderPairNamed(syncEngineConfig.Name);
        }

        public bool HasFolderPairNamed(string folderPairName)
        {
            if (EnableSyncToyIntegration)
            {
                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();
                foreach (var syncEngineConfig in availablePairs)
                {
                    if(syncEngineConfig.Name == folderPairName.Trim())
                    {
                        CurrentFolderPair = syncEngineConfig;
                        CurrentIndex = availablePairs.IndexOf(syncEngineConfig);
                        return true;
                    }
                }
            }
            return false;
        }

        public bool UpdateFolderPair(FileSyncPairInfo fileSyncPairInfo)
        {
            if (EnableSyncToyIntegration && HasFolderPair(fileSyncPairInfo))
            {
                var synctoyPair = CurrentFolderPair;
                CrSyncFilesUtility.SynchronizePairsFrom(synctoyPair, fileSyncPairInfo);
                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();
                availablePairs[CurrentIndex] = synctoyPair;
                CrSyncFilesUtility.SetSyncToyFolderPairs(availablePairs);
                return true;
            }
            return false;
        }

        public bool UpdateFolderPair(SyncEngineConfig synctoyPair)
        {
            if (EnableSyncToyIntegration && HasFolderPair(synctoyPair))
            {
                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();
                availablePairs[CurrentIndex] = synctoyPair;
                CrSyncFilesUtility.SetSyncToyFolderPairs(availablePairs);
                return true;
            }
            return false;
        }

        public bool RemoveFolderPair(FileSyncPairInfo fileSyncPairInfo)
        {
            if (EnableSyncToyIntegration && HasFolderPair(fileSyncPairInfo))
            {
                //CrSyncFilesUtility.SynchronizePairsFrom(CurrentFolderPair, fileSyncPairInfo);
                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();
                availablePairs.RemoveAt(CurrentIndex);

                // Remove markers and meta data
                CrSyncFilesUtility.RemoveSupplementaryFiles(CurrentFolderPair);
                
                CrSyncFilesUtility.SetSyncToyFolderPairs(availablePairs);
                return true;
            }
            return false;
        }

        public  bool RemoveFolderPairNamed(string folderPairName)
        {
            if(HasFolderPairNamed(folderPairName))
            {
                // Remove markers and meta data
                CrSyncFilesUtility.RemoveSupplementaryFiles(CurrentFolderPair);

                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();
                availablePairs.RemoveAt(CurrentIndex);
                CrSyncFilesUtility.SetSyncToyFolderPairs(availablePairs);
                return true;
            }
            return false;
        }


        /// <summary>
        /// Converts and appends a FileSyncPairInfo object to SyncToy's database as a newly created SyncToy folder pair.
        /// All meta files are taken care here.
        /// </summary>
        /// <param name="fileSyncPairInfo"></param>
        /// <returns></returns>
        public bool AddFolderPair(FileSyncPairInfo fileSyncPairInfo)
        {
            SyncEngineConfig synctoyPair;
            return AddFolderPair(fileSyncPairInfo, out synctoyPair);
        }

        public bool AddFolderPair(FileSyncPairInfo fileSyncPairInfo, out SyncEngineConfig synctoyPair)
        {
            if (EnableSyncToyIntegration && (!HasFolderPair(fileSyncPairInfo)))
            {
                var newSyncToyPair = CrSyncFilesUtility.ConvertToSyncToy(fileSyncPairInfo);
                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();

                // Create Marker files
                var leftMarker = CrSyncFilesUtility.GetSyncToyMarkerFilePath(fileSyncPairInfo.LeftFolder.Path, newSyncToyPair.LeftGuid);
                var rigthMarker = CrSyncFilesUtility.GetSyncToyMarkerFilePath(fileSyncPairInfo.RightFolder.Path, newSyncToyPair.RightGuid);

                if(Directory.Exists(fileSyncPairInfo.LeftFolder.Path))
                {
                    using (FileStream leftStream = File.Create(leftMarker))
                    {
                        leftStream.Write(newSyncToyPair.LeftGuid.ToByteArray(), 0,
                                         newSyncToyPair.LeftGuid.ToByteArray().Length);
                    }
                    File.SetAttributes(leftMarker, FileAttributes.Hidden);
                }

                if(!rigthMarker.StartsWith("\\\\Cloud"))
                {
                    if(Directory.Exists(fileSyncPairInfo.RightFolder.Path))
                    {
                        using (var rightStream = File.Create(rigthMarker))
                        {
                            rightStream.Write(newSyncToyPair.RightGuid.ToByteArray(), 0, newSyncToyPair.RightGuid.ToByteArray().Length);
                        }
                        File.SetAttributes(rigthMarker, FileAttributes.Hidden);
                    }
                }

                // Create the meta data
                CreateSyncProvider(newSyncToyPair);

                newSyncToyPair.IsLeftMetadataFileInCentralLocation = true;
                newSyncToyPair.IsLeftReplicaFileMarkerCreated = true;
                newSyncToyPair.IsRightMetadataFileInCentralLocation = true;
                newSyncToyPair.IsRightReplicaFileMarkerCreated = true;

                availablePairs.Add(newSyncToyPair);
                CrSyncFilesUtility.SetSyncToyFolderPairs(availablePairs);

                synctoyPair = newSyncToyPair;
                return true;
            }
            synctoyPair = null;
            return false;
        }

        public void CreateSyncProvider(SyncEngineConfig synctoyPair)
        {
            var explicitDetectChanges = FileSyncOptions.ExplicitDetectChanges;
            if (synctoyPair.ComputeHash)
            {
                explicitDetectChanges |= FileSyncOptions.CompareFileStreams;
            }
            
            if (synctoyPair.BackupOlderFile)
            {
                explicitDetectChanges |= FileSyncOptions.RecycleConflictLoserFiles | FileSyncOptions.RecyclePreviousFileOnUpdates | FileSyncOptions.RecycleDeletedFiles;
            }
            
            var filter = new FileSyncScopeFilter();

            if(!string.IsNullOrEmpty(synctoyPair.ExcludedPattern))
            {
                foreach (var pattern in synctoyPair.ExcludedPattern.Split(new[] { ',', ';' }))
                {
                    filter.FileNameExcludes.Add(pattern);
                }
            }

            if(!string.IsNullOrEmpty(synctoyPair.SearchPattern))
            {
                foreach (var pattern in synctoyPair.SearchPattern.Split(new[] { ',', ';' }))
                {
                    filter.FileNameIncludes.Add(pattern);
                }
            }

            // Initialize left meta data
            if(!synctoyPair.LeftDir.StartsWith("\\\\Cloud"))
            {
                if(Directory.Exists(synctoyPair.LeftDir))
                {
                    new FileSyncProvider(synctoyPair.LeftGuid, synctoyPair.LeftDir, filter,
                                 explicitDetectChanges,
                                 CrSyncFilesUtility.GetSyncToyMetaDataDirectory(),
                                 string.Format("SyncToy_{0}.dat", synctoyPair.LeftGuid),
                                 synctoyPair.LeftDir, null);
                }
            }


            // Initialize right meta data
            if(!synctoyPair.RightDir.StartsWith("\\\\Cloud"))
            {
                if(Directory.Exists(synctoyPair.RightDir))
                {
                    new FileSyncProvider(synctoyPair.RightGuid,
                                     synctoyPair.RightDir,
                                     filter,
                                     explicitDetectChanges,
                                     CrSyncFilesUtility.GetSyncToyMetaDataDirectory(),
                                     string.Format("SyncToy_{0}.dat", synctoyPair.RightGuid),
                                     synctoyPair.RightDir,
                                     null);
                }
            }
        }



        
        public bool RenameFolderPair(string oldName, string newName)
        {
            if(HasFolderPairNamed(newName))
            {
                return false;
            }
            
            if (HasFolderPairNamed(oldName))
            {
                CurrentFolderPair.Name = newName;
                var availablePairs = CrSyncFilesUtility.GetSyncToyFolderPairs();
                availablePairs[CurrentIndex] = CurrentFolderPair;
                CrSyncFilesUtility.SetSyncToyFolderPairs(availablePairs);
                return true;
            }

            return false;
        }


        public List<SyncEngineConfig> GetAvailableFolderPairs()
        {
            return CrSyncFilesUtility.GetSyncToyFolderPairs();
        }

        public void Clear()
        {
            var emptyFolderPairs = new List<SyncEngineConfig>();
            CrSyncFilesUtility.SetSyncToyFolderPairs(emptyFolderPairs);
        }
    }
}
